package com.lhn.case2;

import org.apache.zookeeper.*;
import org.apache.zookeeper.client.ZKClientConfig;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class DistributedLock {

    private final String connectString = "192.168.3.33:2181";
    private final int sessionTimeout = 2000;
    private final ZooKeeper zooKeeper;
    private final String rootNode = "locks";
    private final String subNode = "seq-";

    private CountDownLatch connectLatch = new CountDownLatch(1);
    private CountDownLatch waitLatch = new CountDownLatch(1);
    private String waitPath;
    private String currentNode;


    public DistributedLock() throws IOException, KeeperException, InterruptedException {
        ZKClientConfig config = new ZKClientConfig();
        config.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, "false");
        // 获取连接
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                // 连接建立时, 打开 latch, 唤醒 wait 在该 latch 上的线程
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    connectLatch.countDown();
                }
                // 发生了 waitPath 的删除事件
                if (event.getType() == Event.EventType.NodeDeleted && event.getPath().equals(waitPath)) {
                    waitLatch.countDown();
                }
            }
        }, config);
        connectLatch.await();
        // 判断节点/locks 是否存在
        Stat stat = zooKeeper.exists("/" + rootNode, false);
        // 如果根节点不存在则创建永久根节点
        if (stat == null) {
            System.out.println("根节点不存在！");
            zooKeeper.create("/" + rootNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }

    }

    // 获取锁
    public void zkLock() throws KeeperException, InterruptedException {
        // 在根节点下创建临时顺序节点，返回值为创建的节点路径
        currentNode = zooKeeper.create("/" + rootNode + "/" + subNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

        // 获取所有的节点
        List <String> children = zooKeeper.getChildren("/" + rootNode, false);

        // 列表中只有一个节点，就直接获取到锁
        if (children.size() == 0) {
            return;
        } else {
            // 对节点进行排序
            Collections.sort(children);
            //当前节点名称
            String thisNode = currentNode.substring(("/" + rootNode + "/").length());
            // 获取当前节点在数组中的位置
            int indexOf = children.indexOf(thisNode);
            if (indexOf == -1) {
                System.out.println("数据异常");
            } else if (indexOf == 0) {
                // index == 0 说明 thisNode 在列表中最小，当前 client 获取锁
                return;
            } else {
                // 获得排名比 currentNode 前 1 位的节点
                this.waitPath = "/" + rootNode + "/" + children.get(indexOf - 1);
                // 在 waitPath 上注册监听器, 当 waitPath 被删除时,zookeeper 会回调监听器的 process 方法
                zooKeeper.getData(waitPath, true, new Stat());
                waitLatch.await();
                return;
            }

        }

    }

    // 释放锁
    public void unZkLock() {
        try {
            zooKeeper.delete(this.currentNode, -1);
        } catch (InterruptedException | KeeperException e) {
            e.printStackTrace();
        }
    }


}